package com.ky.pro.java.nio.http.server;

import com.ky.pro.java.nio.http.server.filter.FilterManager;
import com.ky.pro.java.nio.http.server.outbound.client.IGatewayClient;
import com.ky.pro.java.nio.http.server.router.GatewayRouter;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;

import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * @ClassName NettyHttpHandler
 * @Description TODO
 * @Date 2021/11/13 23:05
 * @Author kevin
 **/
@ChannelHandler.Sharable
public class NettyHttpHandler extends ChannelInboundHandlerAdapter {

    private IGatewayClient httpClient;

    private GatewayRouter router;

    private FilterManager filterManager;

    public NettyHttpHandler(IGatewayClient httpClient, GatewayRouter router, FilterManager filterManager) {
        this.httpClient = httpClient;
        this.router = router;
        this.filterManager = filterManager;
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        FullHttpRequest request = (FullHttpRequest) msg;
        String uri = request.uri();

        handleInboundFilters(request, ctx);

        String serviceName = parseServiceName(uri);

        InetSocketAddress endpointAddress = router.findServiceAddress(serviceName);

        HttpResponse response;
        if (endpointAddress == null) {
            response = handleNotFound(request, ctx);
        } else {
            response = httpClient.sendRequest(request, endpointAddress);
        }

        handleOutboundFilters(response, ctx);

        if (!HttpUtil.isKeepAlive(request)) {
            ctx.write(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            response.headers().set(CONNECTION, KEEP_ALIVE);
            ctx.write(response);
        }
        ctx.flush();
    }

    private String parseServiceName(String uri) {
        String[] uriParts = StringUtils.split(uri, "/");
        if (uriParts.length >= 1) {
            return uriParts[0];
        }
        return null;
    }

    private void handleInboundFilters(HttpRequest request, ChannelHandlerContext ctx) {
        filterManager.inboundFilter().forEach(filter -> filter.filter(request, ctx));
    }

    private void handleOutboundFilters(HttpResponse response, ChannelHandlerContext ctx) {
        filterManager.outboundFilters().forEach(filter -> filter.filter(response, ctx));
    }

    private HttpResponse handleNotFound(FullHttpRequest fullRequest, ChannelHandlerContext ctx) {

        String body = "{\"status\":\"err\", \"data\": {\"msg\":\"not found\"}}";
        FullHttpResponse response = null;
        try {
            response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.NOT_FOUND,
                    Unpooled.wrappedBuffer(body.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException e2) {
            // won't happen!
        }
        response.headers().set("Content-Type", "application/json");
        response.headers().setInt("Content-Length", response.content().readableBytes());
        return response;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
